﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	Context.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using Microsoft.SharePoint;
using Smart.Common.Entities;

namespace Smart.Common
{
	/// <summary>
	/// Scope context
	/// </summary>
	[Localizable(false), DebuggerDisplay("Context Name={Name}")]
	public abstract class EntityScope : IEntityScope
	{
		/// <summary>
		/// Get and init scope context by it's name
		/// </summary>
		/// <typeparam name="TScopeType">Scope Context Type</typeparam>
		/// <param name="scopeName">Scope name</param>
		/// <returns>Scope context</returns>
		public static TScopeType GetScope<TScopeType>(string scopeName)
			where TScopeType : class, IEntityScope
		{
			return GetScope<TScopeType>(SPServices.Current, scopeName);
		}

		/// <summary>
		/// Get and init scope context by it's name
		/// </summary>
		/// <typeparam name="TScopeType">Scope Context Type</typeparam>
		/// <param name="scopeName">Scope name</param>
		/// <param name="scopeSettings">Scope settings</param>
		/// <returns>Scope context</returns>
		public static TScopeType GetScope<TScopeType>(string scopeName, string[] scopeSettings)
			where TScopeType : class, IEntityScope
		{
			return GetScope<TScopeType>(SPServices.Current, scopeName, scopeSettings);
		}

		/// <summary>
		/// Get and init scope context by it's name
		/// </summary>
		/// <typeparam name="TScopeType">Scope Context Type</typeparam>
		/// <param name="services">SPServices instance</param>
		/// <param name="scopeName">Scope name</param>
		/// <returns>Scope context</returns>
		public static TScopeType GetScope<TScopeType>(SPServicesInstance services, string scopeName)
			where TScopeType : class, IEntityScope
		{
			var scopeSettings = GetScopeSettings(services, scopeName);

			if (scopeSettings == null || scopeSettings.Length <= 0)
				throw new ConfigurationErrorsException("scopeSettings is empty!");

			return GetScope<TScopeType>(services, scopeName, scopeSettings);
		}

		/// <summary>
		/// Return scope settings
		/// </summary>
		protected static string[] GetScopeSettings(SPServicesInstance services, string scopeName)
		{
			var scopeSettingsValue = services.Configuration.GetValue(GetScopeConfigKey(scopeName), null);

			if (scopeSettingsValue.IsNullOrEmpty())
				return null;

			var scopeSettings = scopeSettingsValue.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

			return scopeSettings;
		}

		/// <summary>
		/// Get and init scope context by it's name
		/// </summary>
		/// <typeparam name="TScopeType">Scope Context Type</typeparam>
		/// <param name="services">SPServices instance</param>
		/// <param name="scopeName">Scope name</param>
		/// <param name="scopeSettings">Scope settings</param>
		/// <returns>Scope context</returns>
		public static TScopeType GetScope<TScopeType>(SPServicesInstance services, string scopeName, string[] scopeSettings)
			where TScopeType : class, IEntityScope
		{
			if (scopeSettings == null || scopeSettings.Length <= 0)
				throw new ConfigurationErrorsException("scopeSettings is empty!");

			var scopeTypeName = scopeSettings[0];

			if (scopeTypeName.IsNullOrEmpty())
				throw new ConfigurationErrorsException("scopeTypeName is empty!");

			var context = services.ServiceLocator.GetInstance<TScopeType>(scopeTypeName);

			if (context == null)
				throw new ConfigurationErrorsException("Can't find instance for scopeTypeName \"" + scopeTypeName + "\"!");

			context.InitScope(scopeName, scopeSettings);

			return context;
		}

		/// <summary>
		/// Register scope
		/// </summary>
		public static void Register<TScopeType, TScopeInterface>(string scopeTypeName)
			where TScopeType : class, TScopeInterface, new()
			where TScopeInterface : IEntityScope
		{
			using (new UnsafeUpdate(SPH.GetSite(), SPH.GetRootWeb()))
			{
				var config = SPServices.ServiceLocator;

				config.Register<IEntityScope, TScopeType>(scopeTypeName);
				config.Register<TScopeInterface, TScopeType>(scopeTypeName);
			}
		}

		/// <summary>
		/// Unregister scope
		/// </summary>
		public static void Unregister<TScopeInterface>(string scopeTypeName)
			where TScopeInterface : IEntityScope
		{
			using (new UnsafeUpdate(SPH.GetSite(), SPH.GetRootWeb()))
			{
				var config = SPServices.ServiceLocator;

				config.Unregister<IEntityScope>(scopeTypeName);
				config.Unregister<TScopeInterface>(scopeTypeName);
			}
		}

		/// <summary>
		/// Return scope configuration key
		/// </summary>
		/// <param name="scopeName">Configuration name</param>
		private static string GetScopeConfigKey(string scopeName)
		{
			return string.Format(Const.Context.ScopeConfigKeyTempl, scopeName);
		}

		/// <summary>
		/// Scope Name
		/// </summary>
		public string Name { get; protected set; }

		/// <summary>
		/// Metadata for Scope
		/// </summary>
		public MetaScope Meta { get; protected set; }

		/// <summary>
		/// Mapper for current scope context
		/// </summary>
		public IEntityMapper Mapper { get; protected set; }

		/// <summary>
		/// Facade-ListService mapping
		/// </summary>
		public Dictionary<string, FacadeMappingInfo> FacadeMapping { get; protected set; }

		/// <summary>
		/// Scope context
		/// </summary>
		protected EntityScope()
		{
			FacadeMapping = new Dictionary<string, FacadeMappingInfo>(StringComparer.OrdinalIgnoreCase);
		}

		/// <summary>
		/// Init scope with settings
		/// </summary>
		/// <param name="name">Name of Scope</param>
		/// <param name="settings">Settings, first parameter is scope type name</param>
		public abstract void InitScope(string name, params string[] settings);

		/// <summary>
		/// Return default scope settings
		/// </summary>
		protected abstract string GetDefaultSettings(string scopeTypeName, string scopeName);

		/// <summary>
		/// Set default scope settings
		/// </summary>
		public virtual void SetDefaultSettings(IConfiguration config, SPWeb web, string scopeTypeName, string scopeName)
		{
			var sk = GetScopeConfigKey(scopeName);
			var ds = GetDefaultSettings(scopeTypeName, scopeName);

			config.SetValue(web, sk, ds);
		}

		/// <summary>
		/// List service for current scope context
		/// </summary>
		/// <param name="id">List ID</param>
		public virtual IListService<TEntityType> GetListService<TEntityType>(Guid id) where TEntityType : Entity
		{
			return GetListService<TEntityType>(Meta.List(id));
		}

		/// <summary>
		/// List service for current scope context
		/// </summary>
		/// <param name="listTitleOrUrl">List Title or Url or Guid</param>
		public virtual IListService<TEntityType> GetListService<TEntityType>(string listTitleOrUrl) where TEntityType : Entity
		{
			return GetListService<TEntityType>(Meta.List(listTitleOrUrl));
		}

		/// <summary>
		/// Return entity facade
		/// </summary>
		public IEntityFacade<TEntityType> Get<TEntityType>(string facadeName) where TEntityType : Entity
		{
			var mappingInfo = FacadeMapping.SafeGet(facadeName);

			if (mappingInfo == null)
				throw new NotSupportedException();

			var service = GetListService<TEntityType>(mappingInfo.ListName);

			if (service == null)
				throw new NotSupportedException();

			return CreateFacade(facadeName, service);
		}

		/// <summary>
		/// Return facade metadata
		/// </summary>
		public MetaList GetMeta(string facadeName)
		{
			var mappingInfo = FacadeMapping.SafeGet(facadeName);

			if (mappingInfo == null)
				throw new NotSupportedException();

			return Meta.List(mappingInfo.ListName);
		}

		/// <summary>
		/// Load metadata for scope
		/// </summary>
		protected abstract MetaScope LoadMetaScope();

		/// <summary>
		/// List service for current scope context
		/// </summary>
		protected abstract IListService<TEntityType> GetListService<TEntityType>(MetaList list) where TEntityType : Entity;

		/// <summary>
		/// Create entity facade
		/// </summary>
		protected abstract IEntityFacade<TEntityType> CreateFacade<TEntityType>(string facadeName, IListService<TEntityType> service) where TEntityType : Entity;

		/// <summary>
		/// Return source
		/// </summary>
		public abstract object GetSource(MetaList list);

		/// <summary>
		/// Flag whether class should be disposed
		/// </summary>
		protected volatile bool ShouldBeDisposed;

		/// <summary>
		/// Flag whether class were disposed
		/// </summary>
		protected volatile bool Disposed;

		/// <summary>
		/// Dispose underlying resources
		/// </summary>
		/// <param name="disposing">flag whether method called by user directly</param>
		protected virtual void Dispose(bool disposing)
		{
			if (Disposed)
				return;

			if (ShouldBeDisposed)
			{
				if (disposing)
				{
					DisposeManaged();
				}

				DisposeUnmanaged();
			}

			Disposed = true;
		}

		/// <summary>
		/// Dispose managed resources. Method called only once.
		/// </summary>
		protected virtual void DisposeManaged()
		{
		}

		/// <summary>
		/// Dispose unmanaged resources. Method called only once.
		/// </summary>
		protected virtual void DisposeUnmanaged()
		{
		}

		/// <summary>
		/// Dispose underlying resources
		/// </summary>
		public void Dispose()
		{
			Dispose(true);

			GC.SuppressFinalize(this);
		}

		~EntityScope()
		{
			Dispose(false);
		}
	}
}